package pkg4funlex;

import pkg4fungeneric.Code;
import pkg4funlex.Automaton.*;

/**
 *
 * @author 4FunTeam
 */

public class Analyzer {
    private static int current_state;
    private static Token token;
    private static Automaton current_automaton;
    private static String error;
    public static int[] code;

    public static String getError() {
        return error;
    }

    public static void printError(String error) {
        System.out.println(
                "Lexical error in line "
                + Scanner.getLine_index()
                + ".\nError message: "
                + error);
        System.exit(1);
    }

    public static void setToken(Token token) {
        Analyzer.token = token;
    }

    public static Token getNextToken() {
        char c;
        int len;
        do {
            c = Scanner.getChar();
        } while (sendChar(c) == false);
//        if(c != '\n') { len = token.getSpelling().length(); }
//        else len = -1;
        len = token.getSpelling().length();
        Scanner.setPos(len);
        current_state = 0;
        return token;
    }

    public static void setCurrent_state(int current_state) {
        Analyzer.current_state = current_state;
    }
    
    public static boolean sendChar(char c) {
        if(current_state == 0) {
            switch(code[c]) {
                case Code.WS:
                    current_automaton = new WsAutomaton();
                    break;
                case Code.LETTER:
                    current_automaton = new IdAutomaton();
                    break;
                case Code.REL_OP:
                    current_automaton = new RelOpAutomaton();
                    break;
                case Code.MINUS:
                    return treatMinus();
                case Code.POINT:
                    current_automaton = new LogicAutomaton();
                    break;
                case Code.QUOTE:
                    current_automaton = new QuoteAutomaton();
                    break;
                case Code.DIGIT:
                    current_automaton = new NumberAutomaton("");
                    break;
                case Code.ARITH_OP:
                    if ( c == '+')
                        token = new Token("+", String.valueOf(c));
                    else if ( c == '*')
                        token = new Token("*", String.valueOf(c));
                    else if ( c == '/')
                        token = new Token("/", String.valueOf(c));
                    return true;
                case Code.NEWLINE:
                    Scanner.nextLine();
                    return false;
                case Code.DELIM:
                    if ( c == '(')
                        token = new Token("(", String.valueOf(c));
                    else
                        token = new Token(")", String.valueOf(c));
                    return true;
                case Code.COMMA:
                    token = new Token(",", String.valueOf(c));
                    return true;
                case Code.COLON:
                    if(Scanner.getChar() != ':') 
                        printError("Invalid symbol ':" + String.valueOf(c)+"'");
                    token = new Token("::", "::");
                    return true;
                case Code.COMMENT:
                    Scanner.nextLine();
                    return false;
                case Code.FINISH:
                    setToken(new Token("$", "$"));
                    return true;
                default:
                    printError("Invalid character: " + c);
            }
        }
        current_state = 1;
        return current_automaton.transition(c);
    }

    public static boolean treatMinus() {
        char c = Scanner.getChar();
        if (token == null){
            printError("Invalid number or operator");
        }
        else if(isOperand(c)) { // Se o token anterior for operando ou o proximo
                                // não for numero, considera - operador.
            setToken(new Token("-", "-"));
            return true;
        } else {
            current_automaton = new NumberAutomaton("-");
            current_state = 1;
        }
        return current_automaton.transition(c);
    }


    public static boolean isOperand(char c) {
        return  (  code[c] != Code.DIGIT
                || token.getType().equals("Real")
                || token.getType().equals("Id")
                || token.getSpelling().equals(')')
                || token.getType().equals("Integer"));
    }

    public static boolean isValidChar(char c) {
       return(
              (Analyzer.code[c] == Code.DELIM)
              || (Analyzer.code[c] == Code.REL_OP)
              || (Analyzer.code[c] == Code.ARITH_OP)
              || (Analyzer.code[c] == Code.MINUS)
              || (Analyzer.code[c] == Code.WS)
              || (Analyzer.code[c] == Code.NEWLINE)
              || (Analyzer.code[c] == Code.COMMENT));
       /*
        *
    public static final int LETTER = 1;
    public static final int DIGIT = 2;
    public static final int REL_OP = 3;
    public static final int ARITH_OP = 4;
    public static final int DELIM = 5;
    public static final int WS = 6;
    public static final int COMMENT = 7;
    public static final int NEWLINE = 8;
    public static final int MINUS = 9;
    public static final int PUNCT = 10;
    public static final int MORTE = 26;
        */
    }
    
    public static void init() {
        code = new int[1<<14];
        createCodeTable();
        current_state = 0;
    }

    private static void createCodeTable() {
        char[] relacional = {'<', '>', '=', '/'};
        char[] aritmethic = {'+', '*', '/'};
        char[] delimit = {'(', ')'};
        char[] whitespace = {' ', '\t'};
        int i;


        /* Cria uma vetor que para cada caracter usado associa um codigo
         * pra uso do automato que detecta lexemas. */
        for(i = (int) 'a'; i <= (int) 'z'; i++) {
            code[i] = Code.LETTER;
        }
        for(i = (int) 'A'; i <= (int) 'Z'; i++) {
            code[i] = Code.LETTER;
        }
        for(i = (int) '0'; i <= (int) '9'; i++) {
            code[i] = Code.DIGIT;
        }

        for(char c : relacional) {
            code[(int) c] = Code.REL_OP;
        }
        for(char c : aritmethic) {
            code[(int) c] = Code.ARITH_OP;
        }
        for(char c : whitespace) {
            code[(int) c] = Code.WS;
        }
        for(char c : delimit) {
            code[(int) c] = Code.DELIM;
        }
        code[(int) '_'] = Code.LETTER;
        code[(int) '-'] = Code.MINUS;
        code[(int) '.'] = Code.POINT;
        code[(int) '!'] = Code.COMMENT;
        code[(int) '\n'] = Code.NEWLINE;
        code[(int) '"'] = Code.QUOTE;
        code[(int) ','] = Code.COMMA;
        code[(int) ':'] = Code.COLON;
        code[26] = Code.FINISH;
    }
}
